﻿namespace LinqToSqlQueryVisualizer
{
    using System.Data.Linq;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.Serialization.Formatters.Binary;
    using Microsoft.VisualStudio.DebuggerVisualizers;

    /// <summary>
    /// The visualizer proxy for the general query visualizer.
    /// It is responsible for serializing queries to a stream.
    /// It delegates this task to the relevant provider specific query visualizer helper.
    /// </summary>
    public class SourceChooser : VisualizerObjectSource
    {
        public static void SerializeTheQuery(object target, Stream stream)
        {
            // get query
            var query = target as IQueryable;
            if (query == null)
            {
                Error(stream, "Query visualizer invoked on non-IQueryable target.");
                return;
            }

            // get provider
            var type = query.GetType();
            var fieldInfo = type.GetField("context", BindingFlags.NonPublic | BindingFlags.Instance);
            if (fieldInfo == null)
            {
                Error(stream, "Query field 'context' not found in type " + type + ".");
                return;
            }

            var objProvider = fieldInfo.GetValue(query);
            if (objProvider == null)
            {
                Error(stream, "Query field 'context' returned null.");
                return;
            }

            var dataContext = objProvider as DataContext;
            if (dataContext == null)
            {
                Error(stream, "Query is not against a DataContext.");
                return;
            }

            // call visualizer to serialize query info
            Visualizer.StreamQueryInfo(dataContext, query, stream);
        }

        /// <summary>
        /// This method has to write the information about the object to visualize to the stream which is 
        /// then sent to the visualizer.
        /// This implementation retrieves the provider from the query and looks for the QueryVisualizer attribute.
        /// The attribute contains the assembly and class name of the provider specific query visualizer helper.
        /// These data are written to the stream and then this class is loaded and called to do the actual 
        /// serialization of the query.
        /// </summary>
        /// <param name="target">The object to serialize, should be a query.</param>
        /// <param name="outgoingData">Stream that receives the information about the query.</param>
        public override void GetData(object target, Stream outgoingData)
        {
            SerializeTheQuery(target, outgoingData);
        }

        private static void Error(Stream str, string message)
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(str, "None");
            formatter.Serialize(str, message);
            return;
        }
    }
}